package com.crt.nexus.security.component;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @author Genko
 */
@EnableConfigurationProperties({PermitUrlProperties.class, SecurityProperties.class})
public class ResourceServerAutoConfiguration {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    public ResourceServerAccessDeniedHandler resourceServerAccessDeniedHandler(ObjectMapper objectMapper) {
        return new ResourceServerAccessDeniedHandler(objectMapper);
    }

    @Bean
    public PermitUrlBearerTokenExtractor resourceServerBearerTokenExtractor(PermitUrlProperties urlProperties) {
        return new PermitUrlBearerTokenExtractor(urlProperties);
    }

    @Bean
    public ResourceServerExceptionEntryPoint resourceServerExceptionEntryPoint(ObjectMapper objectMapper) {
        return new ResourceServerExceptionEntryPoint(objectMapper);
    }

    @Bean
    public ResourceServerAuthenticationConverter resourceServerAuthenticationConverter() {
        return new ResourceServerAuthenticationConverter();
    }

    @Bean
    @Primary
    @LoadBalanced
    public RestTemplate lbRestTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
            @Override
            public void handleError(@NonNull ClientHttpResponse response) throws IOException {
                if (response.getRawStatusCode() == HttpStatus.FAILED_DEPENDENCY.value() || response.getRawStatusCode() != HttpStatus.BAD_REQUEST.value()) {
                    return;
                }
                // 原有异常处理逻辑
                super.handleError(response);
            }
        });
        return restTemplate;
    }

    @Bean
    public TokenStore tokenStore(SecurityProperties securityProperties) {
        return new JwtTokenStore(jwtAccessTokenConverter(securityProperties));
    }

    private JwtAccessTokenConverter jwtAccessTokenConverter(SecurityProperties securityProperties) {
        DefaultAccessTokenConverter defaultConverter = new DefaultAccessTokenConverter();
        defaultConverter.setUserTokenConverter(new ResourceServerAuthenticationConverter());
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setVerifier(new RsaVerifier(getPublicKeyAsString(securityProperties)));
        converter.setAccessTokenConverter(defaultConverter);
        return converter;
    }

    @SneakyThrows
    private String getPublicKeyAsString(SecurityProperties securityProperties) {
        return IOUtils.toString(securityProperties.getPublicKey().getInputStream(), UTF_8);
    }

}
